package com.sky.sd.system.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.sd.system.bean.dto.RoleDto;
import com.sky.sd.system.bean.dto.RoleOfMenuDto;
import com.sky.sd.system.bean.po.*;
import com.sky.sd.system.bean.vo.DeleteVo;
import com.sky.sd.system.bean.vo.RoleSearchVo;
import com.sky.sd.system.bean.vo.RoleToMenusVo;
import com.sky.sd.system.bean.vo.RoleToPermsVo;
import com.sky.sd.system.constant.SystemConstant;
import com.sky.sd.system.dao.MenuMapper;
import com.sky.sd.system.dao.RoleMapper;
import com.sky.sd.system.dao.RoleMenuMapper;
import com.sky.sd.system.dao.RoleUserMapper;
import com.sky.sd.system.enums.EnabledType;
import com.sky.sd.system.enums.MenuType;
import com.sky.sd.system.service.IRoleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统角色 服务实现类
 * </p>
 *
 * @author 弓振
 * @since 2020-06-30
 */
@Service
@Slf4j
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleUserMapper roleUserMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Override
    public IPage<RoleDto> listRolePage(RoleSearchVo vo) {
        IPage<RoleDto> page = new Page<>(vo.getPageNum(), vo.getPageSize());
        page =  baseMapper.listRolePage(page, vo);
        page.getRecords().parallelStream().forEach(roleDto -> {
            roleDto.setUserCnt(roleUserMapper.selectCount(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getRoleId, roleDto.getId())));
        });
        return page;
    }

    @Override
    public Set<String> delete(DeleteVo vo) {
        Set<String> ids = vo.getIds();
        Set<String> noDeleteIds = new HashSet<>();
        Set<String> deleteIds = new HashSet<>();
        ids.parallelStream().forEach(id -> {
            int userCnt = roleUserMapper.selectCount(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getRoleId, id));
            if(userCnt> 0) {
                noDeleteIds.add(id);
            } else {
                deleteIds.add(id);
            }
        });
        if(CollectionUtils.isNotEmpty(deleteIds)) {
            baseMapper.deleteBatchIds(deleteIds);
        }

        if(CollectionUtils.isNotEmpty(noDeleteIds)) {
            return baseMapper.selectList(Wrappers.<Role>lambdaQuery().in(Role::getId, noDeleteIds))
                    .parallelStream().map(Role::getName).collect(Collectors.toSet());
        } else {
            return (Set<String>) CollectionUtils.EMPTY_COLLECTION;
        }
    }

    @Override
    public List<RoleOfMenuDto> roleOfMenus(String roleId) {
        List<Menu> menus = menuMapper.listMenusByRoleIds(new HashSet<>(Arrays.asList(roleId)), MenuType.MENU);
        List<RoleOfMenuDto> roleOfMenus = new ArrayList<>();
        initMenu(roleOfMenus,menus);
        return roleOfMenus;
    }

    @Override
    public void roleToMenus(RoleToMenusVo vo) {
        roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, vo.getId()));
        vo.getMenuIds().parallelStream().forEach(menuId -> {
            roleMenuMapper.insert(RoleMenu.builder().roleId(vo.getId()).menuId(menuId).build());
        });
    }

    @Override
    public Set<String> roleOfPermsByMenuId(String roleId, String menuId) {
        // 查询当前菜单下所有权限
        List<Menu> permsOfMenuId = new ArrayList<>();
        listPermsByMenuId(menuId, permsOfMenuId);
        // 查询角色所关联的所有权限,获取二者的并集
        List<Menu> permsOfRoleId = menuMapper.listMenusByRoleIds(new HashSet<>(Arrays.asList(roleId)), MenuType.BUTTON);
        // 二者取并集
        Set<String> permIdsOfMenuId = permsOfMenuId.parallelStream().map(permOfMenuId -> permOfMenuId.getId()).collect(Collectors.toSet());
        log.info("根据菜单：{} 获取权限{}",menuId, permIdsOfMenuId);
        Set<String> permIdsOfRoleId = permsOfRoleId.parallelStream().map(permOfRoleId -> permOfRoleId.getId()).collect(Collectors.toSet());
        log.info("根据角色：{} 获取权限{}",roleId, permIdsOfRoleId);
        Set<String> permIds = new HashSet<>(CollectionUtils.intersection(permIdsOfMenuId,permIdsOfRoleId));
        log.info("交集{}",permIds);
        return permIds;
    }

    @Override
    public void roleToPermsByMenuId(RoleToPermsVo vo) {
        // 获取旧的当前菜单下角色所关联的权限
        Set<String> permIds = roleOfPermsByMenuId(vo.getId(), vo.getMenuId());
        // 前台传输过来的和旧的权限取差集，新增
        log.info("前台传的新的权限集合为{}",vo.getPermIds());
        Set<String> newPermIds = new HashSet<>(CollectionUtils.subtract(vo.getPermIds(), permIds));
        log.info("前台传的新权限-老权限 求差集{}",newPermIds);
        newPermIds.parallelStream().forEach(newPermId -> {
            int insertCnt = roleMenuMapper.insert(RoleMenu.builder().menuId(newPermId).roleId(vo.getId()).build());
            log.info("新增条数：{}",insertCnt);
        });

        // 前台传输过来的和旧的权限取补集，删除
        Set<String> oldPermIds = new HashSet<>(CollectionUtils.subtract(permIds, vo.getPermIds()));
        log.info("老权限-前台传的新权限 求差集{}",oldPermIds);
        oldPermIds.parallelStream().forEach(oldPermId -> {
            int deleteCnt = roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId,vo.getId()).eq(RoleMenu::getMenuId, oldPermId));
            log.info("删除条数：{}",deleteCnt);
        });
    }

    private void listPermsByMenuId(String menuId, List<Menu> allPerms) {
        List<Menu> childPerms = menuMapper.selectList(Wrappers.<Menu>lambdaQuery()
                .eq(Menu::getEnabled,EnabledType.ENABLED).eq(Menu::getType, MenuType.BUTTON).eq(Menu::getParentId,menuId));
        childPerms.parallelStream().forEach(menu -> {
            listPermsByMenuId(menu.getId(), allPerms);
        });
        allPerms.addAll(childPerms);
    }

    private void initMenu(List<RoleOfMenuDto> roleOfMenus, List<Menu> menus) {
        List<Menu> curMenus = menus.parallelStream().filter(menu -> SystemConstant.TOP_PARENT_ID.equals(menu.getParentId())).collect(Collectors.toList());

        curMenus.parallelStream().forEach(curMenu -> {
            createMenu(roleOfMenus, menus, curMenu);
        });
    }

    private void createMenu(List<RoleOfMenuDto> roleOfMenus, List<Menu> menus, Menu curMenu) {
        List<Menu> curMenus = menus.parallelStream().filter(menu -> curMenu.getId().equals(menu.getParentId())).collect(Collectors.toList());
        roleOfMenus.add(RoleOfMenuDto.builder().id(curMenu.getId()).parentId(curMenu.getParentId()).leaf(CollectionUtils.isNotEmpty(curMenus) ? 0 : 1).build());
        curMenus.parallelStream().forEach(menu -> {
            createMenu(roleOfMenus, menus, menu);
        });
    }
}
